/*
 * This file is part of Krita
 *
 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <QMouseEvent>

#include <kis_histogram.h>
#include <KoColorSpace.h>
#include <KisHistogramPainter.h>

#include "KisHistogramView.h"

class KisHistogramView::Private
{
public:
    QVector<KisHistogramPainter *> histogramPainters;
    int histogramIndex{0};

    qreal preDraggingScale;
    int draggingStartMouseY;
    bool isScaling{false};
};

KisHistogramView::KisHistogramView(QWidget *parent)
    : QWidget(parent)
    , m_d(new Private)
{}

KisHistogramView::~KisHistogramView()
{
    qDeleteAll(m_d->histogramPainters);
}

void KisHistogramView::setup(const QVector<KisHistogram*> &histograms,
                             const QVector<const KoColorSpace*> &colorSpaces,
                             const QVector<QVector<int>> &channels)
{
    Q_ASSERT(colorSpaces.size() == histograms.size());
    Q_ASSERT(channels.size() == 0 || channels.size() == histograms.size());

    m_d->histogramIndex = 0;
    qDeleteAll(m_d->histogramPainters);
    m_d->histogramPainters.clear();

    if (histograms.size() == 0) {
        return;
    }

    QVector<QVector<int>> autoGeneratedChannels;
    if (channels.size() == 0) {
        for (int i = 0; i < histograms.size(); ++i) {
            autoGeneratedChannels.append(QVector<int>());
        }
    }

    const QVector<QVector<int>> &finalChannels =
        channels.size() == 0 ? autoGeneratedChannels : channels;

    for (int i = 0; i < histograms.size(); ++i) {
        m_d->histogramPainters.append(new KisHistogramPainter());
        m_d->histogramPainters[i]->setup(histograms[i], colorSpaces[i], finalChannels[i]);
    }
}

const QVector<int>& KisHistogramView::channels() const
{
    return m_d->histogramPainters[m_d->histogramIndex]->channels();
}

void KisHistogramView::setChannel(int channel, int histogramIndex)
{
    setChannels({channel}, histogramIndex);
}

void KisHistogramView::setChannels(const QVector<int> &channels, int histogramIndex)
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);
    Q_ASSERT(histogramIndex >= 0 && histogramIndex < m_d->histogramPainters.size());

    const QColor defaultColor = m_d->histogramPainters[m_d->histogramIndex]->defaultColor();
    const bool isLogarithmic = m_d->histogramPainters[m_d->histogramIndex]->isLogarithmic();

    m_d->histogramIndex = histogramIndex;
    m_d->histogramPainters[m_d->histogramIndex]->setChannels(channels);
    m_d->histogramPainters[m_d->histogramIndex]->setDefaultColor(defaultColor);
    m_d->histogramPainters[m_d->histogramIndex]->setLogarithmic(isLogarithmic);
    setScaleToFit();
    
    update();
}

void KisHistogramView::clearChannels()
{
    setChannels({});
}

QColor KisHistogramView::defaultColor() const
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);

    return m_d->histogramPainters[m_d->histogramIndex]->defaultColor();
}

void KisHistogramView::setDefaultColor(const QColor &newDefaultColor)
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);

    m_d->histogramPainters[m_d->histogramIndex]->setDefaultColor(newDefaultColor);
    update();
}

qreal KisHistogramView::scale() const
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);
    
    return m_d->histogramPainters[m_d->histogramIndex]->scale();
}

void KisHistogramView::setScale(qreal newScale)
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);
    
    m_d->histogramPainters[m_d->histogramIndex]->setScale(newScale);
    update();
}

void KisHistogramView::setScaleToFit()
{
    setScale(1.0);
}

void KisHistogramView::setScaleToCutLongPeaks()
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);
    
    m_d->histogramPainters[m_d->histogramIndex]->setScaleToCutLongPeaks();
    update();
}

bool KisHistogramView::isLogarithmic() const
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);
    
    return m_d->histogramPainters[m_d->histogramIndex]->isLogarithmic();
}

void KisHistogramView::setLogarithmic(bool logarithmic)
{
    Q_ASSERT(m_d->histogramPainters.size() > 0);
    
    m_d->histogramPainters[m_d->histogramIndex]->setLogarithmic(logarithmic);
    setScaleToFit();
    update();
}

void KisHistogramView::paintEvent(QPaintEvent *e)
{
    Q_UNUSED(e);

    QPainter painter(this);

    // Background
    painter.fillRect(rect(), palette().base());

    // Histogram
    if (m_d->histogramPainters.size() > 0 &&
        m_d->histogramPainters[m_d->histogramIndex]->channels().size() > 0) {
        // Histogram
        QImage histogramImage = m_d->histogramPainters[m_d->histogramIndex]->paint(size());
        // Shadow
        QLinearGradient shadowGradient(QPointF(0.0, 0.0), QPointF(0.0, static_cast<qreal>(height()) * 0.2));
        shadowGradient.setColorAt(0.00, QColor(0, 0, 0, 64));
        shadowGradient.setColorAt(0.25, QColor(0, 0, 0, 36));
        shadowGradient.setColorAt(0.50, QColor(0, 0, 0, 16));
        shadowGradient.setColorAt(0.75, QColor(0, 0, 0, 4));
        shadowGradient.setColorAt(1.00, QColor(0, 0, 0, 0));
        QPainter histogramPainter(&histogramImage);
        histogramPainter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
        histogramPainter.fillRect(histogramImage.rect(), shadowGradient);
        if (!isEnabled()) {
            painter.setOpacity(0.5);
        }
        painter.drawImage(0, 0, histogramImage);
        painter.setOpacity(1.0);
    } else {
        const qreal w = static_cast<qreal>(width());
        const qreal h = static_cast<qreal>(height());
        const qreal radius = qMin(w, h) * 0.3;
        const QPointF center = QPointF(w / 2.0, h / 2.0);
        const int penWidth = static_cast<int>(qRound(radius / 8));
        painter.setPen(QPen(palette().alternateBase(), penWidth, Qt::SolidLine, Qt::FlatCap));
        painter.setBrush(Qt::NoBrush);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.drawEllipse(center, radius, radius);
        painter.drawLine(center + QPointF(radius, -radius), center + QPointF(-radius, radius));
    }

    // Border
    QColor c = palette().text().color();
    c.setAlpha(64);
    painter.setPen(QPen(c, 1));
    painter.setBrush(Qt::NoBrush);
    painter.setRenderHint(QPainter::Antialiasing, false);
    painter.drawRect(0, 0, width() - 1, height() - 1);
}

void KisHistogramView::mouseDoubleClickEvent(QMouseEvent *e)
{
    if (m_d->histogramPainters.size() == 0 ||
        m_d->histogramPainters[m_d->histogramIndex]->channels().size() == 0) {
        return;
    }

    if (e->button() != Qt::LeftButton) {
        return;
    }

    if (qFuzzyCompare(scale(), 1.0)) {
        setScaleToCutLongPeaks();
    } else {
        setScaleToFit();
    }
}

void KisHistogramView::mousePressEvent(QMouseEvent *e)
{
    if (m_d->histogramPainters.size() == 0 ||
        m_d->histogramPainters[m_d->histogramIndex]->channels().size() == 0) {
        return;
    }

    if (e->button() != Qt::LeftButton) {
        return;
    }

    m_d->preDraggingScale = scale();
    m_d->draggingStartMouseY = e->y();
    m_d->isScaling = false;
}

void KisHistogramView::mouseMoveEvent(QMouseEvent *e)
{
    if (m_d->histogramPainters.size() == 0 ||
        m_d->histogramPainters[m_d->histogramIndex]->channels().size() == 0) {
        return;
    }

    if (!(e->buttons() & Qt::LeftButton)) {
        return;
    }

    if (m_d->isScaling) {
        const qreal newScale =
            m_d->preDraggingScale * static_cast<qreal>(height() - e->y()) /
                static_cast<qreal>(height() - m_d->draggingStartMouseY);
        setScale(qMax(newScale, 1.0));
    } else {
        if (qAbs(e->y() - m_d->draggingStartMouseY) > 4) {
            m_d->isScaling = true;
        }
    }

}
